﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using pwestcott.Util;
using NUnit.Framework;

namespace pwestcott.Util.Tests.Essence.EqualityComparer
{
    [TestFixture]
    public class EqualityComparerTests
    {
        class MyClass
        {
            public MyClass(int i, string s, DateTime d)
            {
                MyInt = i;
                MyString = s;
                MyDateTime = d;
            }

            public int MyInt { get; private set; }
            public string MyString { get; private set; }
            public DateTime MyDateTime { get; private set; }
        }

        class ManualEqualityComparer : IEqualityComparer<MyClass>
        {
            public bool Equals(MyClass lhs, MyClass rhs)
            {
                if (ReferenceEquals(lhs, null))
                {
                    if (ReferenceEquals(rhs, null))
                        return true;
                    return false;
                }
                if (ReferenceEquals(rhs, null))
                    return false;
                if (ReferenceEquals(lhs, rhs))
                    return true;
                if (typeof(MyClass) != rhs.GetType() || typeof(MyClass) != lhs.GetType())
                    return false;

                return lhs.MyInt == rhs.MyInt
                    && lhs.MyString == rhs.MyString
                    && lhs.MyDateTime == rhs.MyDateTime;
            }

            public int GetHashCode(MyClass obj)
            {
                var x = obj.MyInt.GetHashCode();
                x *= Essence<MyClass>.HashCodeMultiplier;
                x ^= (obj.MyString == null) ? 0 : obj.MyString.GetHashCode();
                x *= Essence<MyClass>.HashCodeMultiplier;
                x ^= obj.MyDateTime.GetHashCode();
                return x;
            }
        }

        static readonly int[] ints = { 42, int.MinValue, -1, 93, int.MaxValue };
        static readonly string[] strings = { "Mary", "Mark", "Paul", null, "James" };
        static readonly DateTime[] dates = { new DateTime(1976, 11, 23), DateTime.MaxValue, new DateTime(1973, 11, 13), new DateTime(1999, 4, 18), DateTime.MinValue };

        private void Loop(Action<MyClass> getData)
        {
            for (int i = 0; i < ints.Length; ++i)
            {
                var theInt = ints[i];
                for (int j = 0; j < strings.Length; ++j)
                {
                    var theString = strings[j];
                    for (int k = 0; k < dates.Length; ++k)
                    {
                        var theDate = dates[k];

                        getData(new MyClass(theInt, theString, theDate));
                    }
                }
            }
        }

        [Test]
        public void TestEqualityComparerStrings()
        {
            var manualEqualityComparer = new ManualEqualityComparer();
            var essenceEqualityComparer = Essence<MyClass>.CreateEqualityComparer(new[] { "MyInt", "MyString", "MyDateTime" });

            MyClass myClass = new MyClass(ints[0], strings[0], dates[0]);
            Loop(next =>
            {
                Assert.AreEqual(manualEqualityComparer.GetHashCode(next), essenceEqualityComparer.GetHashCode(next));
                Assert.AreEqual(manualEqualityComparer.Equals(myClass, next), essenceEqualityComparer.Equals(myClass, next));
            });
        }

        [Test]
        public void TestEqualityComparerLambda()
        {
            var manualEqualityComparer = new ManualEqualityComparer();
            var essenceEqualityComparer = Essence<MyClass>.CreateEqualityComparer(x => x.MyInt, x => x.MyString, x => x.MyDateTime);

            MyClass myClass = new MyClass(ints[0], strings[0], dates[0]);
            Loop(next =>
            {
                Assert.AreEqual(manualEqualityComparer.GetHashCode(next), essenceEqualityComparer.GetHashCode(next));
                Assert.AreEqual(manualEqualityComparer.Equals(myClass, next), essenceEqualityComparer.Equals(myClass, next));
            });
        }
    }
}
